package com.hedl.classroom.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import com.alibaba.fastjson2.JSON;
import com.hedl.classroom.base.model.PageParams;
import com.hedl.classroom.dto.SearchCourseParamDto;
import com.hedl.classroom.dto.SearchPageResultDto;
import com.hedl.classroom.po.CourseIndex;
import com.hedl.classroom.service.CourseSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Big Watermelon
 * @description 课程搜索service实现类
 * @date 2023/11/02/17:24
 */
@Slf4j
@Service
public class CourseSearchServiceImpl implements CourseSearchService {

    @Value("${elasticsearch.apiKeys.course.index}")
    private String courseIndexStore;

    @Value("${elasticsearch.apiKeys.course.source_fields}")
    private String sourceFields;

    @Resource
    ElasticsearchClient elasticsearchClient;

    @Override
    public SearchPageResultDto<CourseIndex> queryCoursePubIndex(PageParams pageParams, SearchCourseParamDto searchCourseParamDto) {

        //source源字段过虑
        //String[] sourceFieldsArray = sourceFields.split(",");

        SearchResponse<Object> search;

        //过滤
        List<Query> filter = filterList(searchCourseParamDto);
        //分页
        Long start = (pageParams.getPageNo() - 1) * pageParams.getPageSize();

        try {
             search = elasticsearchClient.search(
                    searchRequest -> searchRequest.index(courseIndexStore)
                            .query(
                                    query->query.bool(
                                            boolQuery->{
                                                if (searchCourseParamDto.getKeywords() == null || "".equals(searchCourseParamDto.getKeywords())){
                                                    boolQuery.must(
                                                            q->q.matchAll(
                                                                    MatchAllQuery.of(pi->pi)
                                                            )
                                                    );
                                                }else {
                                                    boolQuery.must(
                                                            q->q.match(MatchQuery.of(
                                                                    pi->pi.field("all")
                                                                            .query(searchCourseParamDto.getKeywords())
                                                            ))
                                                    );
                                                }
                                                boolQuery.filter(filterList(searchCourseParamDto));
                                                return boolQuery;
                                            }
                                    ))
                            .aggregations("mtAgg"
                                    ,aggregation->aggregation.terms(
                                            termsAggregation->termsAggregation.field("mtName").size(100)
                                    )
                            )
                            .aggregations("stAgg"
                                    ,aggregation->aggregation.terms(
                                            termsAggregation->termsAggregation.field("stName").size(100)
                                    )
                            )
                            //高亮
                            .highlight(Highlight.of(
                                    pi -> pi.fields("name"
                                            , highlightField -> highlightField.preTags("<font class='eslight'>")
                                                    .postTags("</font>")
                                                    .requireFieldMatch(false)
                                    )
                            ))
                            .from(start.intValue())
                            .size(pageParams.getPageSize().intValue())
                    , Object.class);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("课程搜索异常：{}",e.getMessage());
            return new SearchPageResultDto<CourseIndex>(new ArrayList(),0,0,0);
        }

        //结果采集
        HitsMetadata<Object> hits = search.hits();
        List<Hit<Object>> searchHits = hits.hits();
        //记录总数
        long totalHits = hits.total().value();

        //数据列表
        List<CourseIndex> courseIndexList = new ArrayList<>();

        searchHits.forEach(
                searchHit->{
                    String jsonString = JSON.toJSONString(searchHit.source());
                    CourseIndex courseIndex = JSON.parseObject(jsonString, CourseIndex.class);

                    //课程id
                    Long id = courseIndex.getId();
                    //取出名称
                    String name = courseIndex.getName();

                    //取出高亮字段内容
                    //高亮字段只对，name有效，其它不起作用
                    Map<String, List<String>> highlight = searchHit.highlight();
                    if (highlight != null){
                        List<String> highlightList = highlight.get("name");

                        //System.out.println(highlightList);

                        if (highlightList != null){
                            StringBuffer stringBuffer = new StringBuffer();
                            highlightList.forEach(
                                    h->{
                                        stringBuffer.append(h);
                                    }
                            );
                           name = stringBuffer.toString();
                           //System.out.println(name);
                        }
                    }
                    courseIndex.setId(id);
                    courseIndex.setName(name);
                    courseIndexList.add(courseIndex);
                }
        );
        SearchPageResultDto<CourseIndex> pageResult = new SearchPageResultDto<>(courseIndexList,totalHits, pageParams.getPageNo(), pageParams.getPageSize());

        //获取聚合结果
        List<String> mtList = bucketsResult(search, "mtAgg");
        List<String> stList = bucketsResult(search, "stAgg");
        pageResult.setMtList(mtList);
        pageResult.setStList(stList);

        return pageResult;
    }

    private static List<String> bucketsResult (SearchResponse<Object> search,String name){
        List<String> bucketsResultList = new ArrayList<>();
        List<StringTermsBucket> array = search.aggregations().get(name).sterms().buckets().array();
        array.forEach(
                objectBucket->{
                    //获取key,再添加进list集合
                    bucketsResultList.add(objectBucket.key().stringValue());
                }
        );
        return bucketsResultList;
    }

    private static List<Query> filterList(SearchCourseParamDto searchCourseParamDto) {
        List<Query> listQuery = new ArrayList<>();
        if (StringUtils.isNotEmpty(searchCourseParamDto.getMt())) {
            Query query = Query.of(
                    pi -> pi.term(
                            termQuery -> termQuery.field("mtName").value(searchCourseParamDto.getMt())
                    )
            );
            listQuery.add(query);
        }

        if (StringUtils.isNotEmpty(searchCourseParamDto.getSt())) {
            Query query = Query.of(
                    pi -> pi.term(
                            termQuery -> termQuery.field("stName").value(searchCourseParamDto.getSt())
                    )
            );
            listQuery.add(query);
        }

        if (StringUtils.isNotEmpty(searchCourseParamDto.getGrade())) {
            Query query = Query.of(
                    pi -> pi.term(
                            termQuery -> termQuery.field("grade").value(searchCourseParamDto.getGrade())
                    )
            );
            listQuery.add(query);
        }
        return listQuery;
    }
}
